package va_btree;

import btree.*;
import global.PageId;
import global.RID;
import global.SystemDefs;
import global.Vector100Dtype;
import heap.HFBufMgrException;
import heap.HFDiskMgrException;
import heap.HFException;
import heap.InvalidTupleSizeException;
import va.BaseVAScan;
import va.BitSet;
import va.Range;
import va.VAEntry;
import va_file.VAFile;

import java.io.IOException;
import java.util.ArrayList;

public class VABTreeScan extends BaseVAScan {

    public VABTreeFile file;

    public VABTreeScan(VABTreeFile indexFile) {
        this.file = indexFile;
    }

    /*
	 * Calls the default range scan with a btree scanner
	 */
    public ArrayList<RID> VABTreeRangeScan(StringKey lowKey, StringKey highKey, Vector100Dtype target, int distance) throws HFException, HFBufMgrException,
            HFDiskMgrException, IOException, InvalidTupleSizeException, PinPageException, IteratorException, KeyNotMatchException, UnpinPageException, ConstructPageException {
        this.btScan = this.file.new_scan(lowKey, highKey);
        return this.VARangeScan(target, distance, this.file.noOfBits);
    }

    /* Call the default NN scan with a btree scanner
    */
    public ArrayList<RID> VABTreeNNScan(StringKey lowKey, StringKey highKey, Vector100Dtype target, int count) throws HFException, HFBufMgrException,
            HFDiskMgrException, IOException, PinPageException, IteratorException, KeyNotMatchException, UnpinPageException, ConstructPageException {
        this.btScan = this.file.new_scan(lowKey, highKey);
        return this.VANNScan(target, count, this.file.noOfBits);
    }
    
    /*
     * REturn the RIDS of a b-tree range scan. Keys are not included as parameters, so we generate them.
     */
    public ArrayList<RID> VABTreeRangeScanNoKeys(Vector100Dtype target, int distance, int noOfBits) throws HFException, HFBufMgrException, HFDiskMgrException, InvalidTupleSizeException, PinPageException, IteratorException, KeyNotMatchException, UnpinPageException, ConstructPageException, IOException
    {
    	//Get the initial strings for approximation
        String lowKey, highKey;
        Range[] ranges = VAFile.GetDefaultRanges(noOfBits);
        BitSet approx = VABTreeFile.GetMinimumApproximation(target, ranges, distance, noOfBits, new BitSet());
        lowKey = VAEntry.GetApproximationString(approx, noOfBits);

        approx = VABTreeFile.GetMaximumApproximation(target, ranges, distance, noOfBits, new BitSet());
        highKey = VAEntry.GetApproximationString(approx, noOfBits);
        return this.VABTreeRangeScan(new StringKey(lowKey), new StringKey(highKey), target, distance);
    }

    /***
     * TESTING FUNCTIONS
     */

    static int testNumBits; //number of bits used for VAs in our test functions

    //Function for testing VA btree scan
    public static VAEntry createEntry()
    {
        Vector100Dtype vd=new Vector100Dtype();
        BitSet vectorApproximation=new BitSet();
        int i=0;
        for(i=0;i<100;i++)
        {

            double randomDouble = Math.random() * 20000 - 10000;
            vd.setIndex(i, (short)randomDouble);
        }
        System.out.println(vd.toString() + "\n");
        Range ranges[]= VAFile.GetDefaultRanges();
        vectorApproximation=VAEntry.CreateApproximation(vd, ranges, testNumBits);
        PageId pid=new PageId(VABTreeFile.pageNo++);
        RID rid=new RID(pid,VABTreeFile.slotNo++);
        VAEntry entry=new VAEntry(vectorApproximation,rid);
        return entry;
    }

    //Function for testing VA btree scan
    public static VAEntry createEntry(Vector100Dtype vd)
    {
        BitSet vectorApproximation=new BitSet();
        int i=0;

        System.out.println(vd.toString() + "\n");
        Range ranges[]=VAFile.GetDefaultRanges();
        vectorApproximation=VAEntry.CreateApproximation(vd, ranges, testNumBits);
        PageId pid=new PageId(VABTreeFile.pageNo++);
        RID rid=new RID(pid,VABTreeFile.slotNo++);
        VAEntry entry=new VAEntry(vectorApproximation,rid);
        return entry;
    }


    //Test our va_btree_scan class
    public static void main(String[] args) throws GetFileEntryException,PinPageException,ConstructPageException
    {
        String dbPath = "minibase-db";
        SystemDefs sysdef = new SystemDefs(dbPath,10000,10000,"Clock");

        VAEntry[] entries = new VAEntry[1000];

        testNumBits = 4;


        for(int i = 0; i < entries.length/ 2; i++)
            entries[i] = createEntry(); //random entries

        Vector100Dtype vecleast = new Vector100Dtype();
        for(int i = 0; i < 100; i++) {vecleast.setIndex(i, (short)-10000);} //vector where its VA will be all 0

        //Make first half of entries all have 0000.... (all zeroes) to show that Eric's algorithm works in those cases
        for(int i =0; i < entries.length/2; i++){entries[i] = createEntry(vecleast);}

        for(int i = entries.length/2; i < entries.length; i++)
            entries[i] = createEntry(); //random entries

        Vector100Dtype target = new Vector100Dtype();

		/*
		for(int i = 0; i < 50; i++)
		{target.setIndex(i, (short)-10000);}

		for(int i =50; i < 100; i++)
		{target.setIndex(i, (short)10000);}*/

        //random target vector
        for(int i = 0; i < 100; i++)
        {
            double randomDouble = Math.random() * 20000 - 10000;
            target.setIndex(i, (short)randomDouble);
        }

        entries[entries.length - 1] = createEntry(target); //set last entry to be at same point as target, so at least one RID should always return

        try
        {
            VABTreeFile file=new VABTreeFile("testFile9", testNumBits);
            BTreeHeaderPage headerPage= file.getHeaderPage();

            for(int i = 0; i < entries.length; i++)
            {
                file.insert(entries[i]);
            }

            //BT.printAllLeafPages(headerPage);
            //BT.printBTree(headerPage);




            System.out.println("\n\n");

            int distance = 10;

            //Get low key
            Range[] ranges = VAFile.GetDefaultRanges();
            BitSet approx = VABTreeFile.GetMinimumApproximation(target, ranges, distance, testNumBits, new BitSet());
            String lowString = VAEntry.GetApproximationString(approx, testNumBits);

            //get high key
            approx = VABTreeFile.GetMaximumApproximation(target, ranges, distance, testNumBits, new BitSet());
            String highString = VAEntry.GetApproximationString(approx, testNumBits);

            StringKey lowKey = new StringKey(lowString);
            StringKey highKey = new StringKey(highString);

            VABTreeScan scan = new VABTreeScan(file);

            ArrayList<RID> rids = new ArrayList<RID>();


            int run1, run2;

            //Scan using Eric's algorithm for getting low/high keys, counting number of RIDs that are returned
            rids = scan.VABTreeRangeScan(lowKey, highKey, target, distance);
            run1 = rids.size();

            rids= new ArrayList<RID>();
            //Scan without pruning, counting number of RIDs that are returned
            rids = scan.VABTreeRangeScan(null, null, target, distance);
            run2 = rids.size();

            System.out.println("\n\nNumber of returned RIDS run 1: " + run1
                    + "\nNumber of returned RIDS run 2: " + run2);
            //scan.range_scan_RIDs(lowKey, highKey, target, distance, noOfBits)
        }
        catch (Exception e ) {
            e.printStackTrace();
        }
    }
}
